package whf.framework.dao;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.springframework.orm.jpa.JpaCallback;

import whf.framework.exception.FindException;
import whf.framework.log.Log;
import whf.framework.log.LogFactory;
import whf.framework.util.BeanInitialization;
import whf.framework.util.StringUtils;

/**
 * @author King
 * @create Jan 8, 2008 10:19:03 AM
 */
@SuppressWarnings(value={"unchecked"})
public class JpaDaoSupport<T> extends org.springframework.orm.jpa.support.JpaDaoSupport implements DAOSupport<T> {
	private static Log log = LogFactory.getLog(JpaDaoSupport.class);
	
	public JpaDaoSupport() {
		super();
	}
	
	public JpaDaoSupport(EntityManager entityManager) {
		super();
		super.setEntityManager(entityManager);
	}
	
	private EntityManager getEntityManager() {
		return super.getJpaTemplate().getEntityManager();
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#save(whf.framework.entity.IBaseBO)
	 */
	public void save(T entity){
		super.getJpaTemplate().persist(entity);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#load(java.lang.Class, java.io.Serializable)
	 */
	public T load(Class<T> clazz, Serializable key){
		return super.getJpaTemplate().find(clazz, key);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#loadWithoutLazy(java.lang.Class, java.io.Serializable)
	 */
	public T loadWithoutLazy(Class<T> clazz, Serializable key){
		EntityManager entityManager = super.getJpaTemplate().getEntityManager();
		T entity = entityManager.find(clazz, key);
		if(entityManager.isOpen())
			BeanInitialization.initializeBean(entity);
		return entity;
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#modify(whf.framework.entity.IBaseBO)
	 */
	public void modify(T entity){
		super.getJpaTemplate().merge(entity);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#delete(whf.framework.entity.IBaseBO)
	 */
	public void delete(T entity){
		super.getJpaTemplate().remove(entity);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String)
	 */
	public List<T> findWithoutLazy(String queryString){
		return this.findWithoutLazy(queryString, null);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String, int, int)
	 */
	public List<T> findWithoutLazy(String queryString, int start, int pageSize){
		return this.findWithoutLazy(queryString, null, start, pageSize);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String, java.lang.Object)
	 */
	public List<T> findWithoutLazy(String queryString, Object arg){
		return this.findWithoutLazy(queryString, new Object[]{arg});
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String, java.lang.Object, int, int)
	 */
	public List<T> findWithoutLazy(String queryString, Object arg, int start, int pageSize){
		return this.findWithoutLazy(queryString, new Object[]{arg}, start, pageSize);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String, java.lang.Object[])
	 */
	public List<T> findWithoutLazy(String queryString, Object[] params){
		return this.findWithoutLazy(queryString, params, 0, Integer.MAX_VALUE);
	}
	
	/**
	 * @modify wanghaifeng Aug 25, 2006 1:10:20 PM
	 * @param queryString
	 * @param params
	 * @param start
	 * @param pageSize
	 * @return
	 */
	public List<T> findWithoutLazy(String queryString, Object[] params, int start, int pageSize){
		EntityManager em = this.getEntityManager();
		Query query = em.createQuery(queryString);
		for(int i=0;params!= null && i< params.length; i++){
			query.setParameter(i, params[i]);
		}
		query.setFirstResult(start);
		query.setMaxResults(pageSize);
		List result = query.getResultList();
		if(em.isOpen())
			for(Iterator it = result.iterator(); it.hasNext(); ){
				BeanInitialization.initializeBean(it.next());
			}
		return result;
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithListResult(java.lang.String)
	 */
	public List<T> findWithListResult(String query){
		return super.getJpaTemplate().find(query);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithListResult(java.lang.String, java.lang.Object)
	 */
	public List<T> findWithListResult(String query, Object arg){
		return super.getJpaTemplate().find(query,arg);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithListResult(java.lang.String, java.lang.Object[])
	 */
	public List<T> findWithListResult(String query, Object[] args){
		return super.getJpaTemplate().find(query,args);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#list(java.lang.String, int, int)
	 */
	public List<T> list(String queryString, int start, int pageSize) {
		List list = this.getJpaTemplate().executeFind(
				new JpaCallBackAction(queryString, start, pageSize) {
					public Object doInJpa(EntityManager entityManager) throws PersistenceException{
						Query query = entityManager.createQuery(getQueryString());
						query.setFirstResult(this.getStart());
						query.setMaxResults(this.getRowsPerPage());
						return query.getResultList();
					}
				});
		return list;
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#list(java.lang.String, java.lang.Object[], int, int)
	 */
	public List<T> list(String queryString, Object[] paramValues, int start, int pageSize) {
		List list = this.getJpaTemplate().executeFind(
				new JpaCallBackAction(queryString, paramValues, start, pageSize) {
					public Object doInJpa(EntityManager entityManager) throws PersistenceException{
						Query query = entityManager.createQuery(getQueryString());
						Object[] paramValues = getParamValues();
						for (int i = 0; paramValues != null && i < paramValues.length; i++) {
							query.setParameter(i, paramValues[i]);
						}
						query.setFirstResult(this.getStart());
						query.setMaxResults(this.getRowsPerPage());
						return query.getResultList();
					}
				});
		return list;
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#list(java.lang.String, java.lang.String[], java.lang.Object[], int, int)
	 */
	public List<T> list(String queryString, String[] paramNames,
			Object[] paramValues, int start, int pageSize) {
		List list = this.getJpaTemplate().executeFind(
				new JpaCallBackAction(queryString, paramNames, paramValues, start, pageSize) {
					public Object doInJpa(EntityManager entityManager) throws PersistenceException{
						Query query = entityManager.createQuery(getQueryString());
						Object[] paramValues = getParamValues();
						String[] paramNames = getParamNames();
						for (int i = 0; paramNames != null && i < paramNames.length; i++) {
							query.setParameter(paramNames[i], paramValues[i]);
						}
						query.setFirstResult(this.getStart());
						query.setMaxResults(this.getRowsPerPage());
						return query.getResultList();
					}
				});
		return list;
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#queryWithoutLazy(java.lang.Class, java.lang.String, int, int)
	 */
	public List<T> queryWithoutLazy(Class boClass, String queryString, int start,
			int pageSize) throws FindException{
		Object[] sqlAndParams = DaoHelper.buildSqlForFindByPropertyString(boClass, queryString);
		String sql = sqlAndParams[0].toString();
		Object[] params = (Object[])sqlAndParams[1];
		List<T> list = this.findWithoutLazy(sql, params, start, pageSize);
		for(T entity: list){
			BeanInitialization.initializeBean(entity);
		}
		return list;
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findBySql(java.lang.String, int, int)
	 */
	public List<T> findBySql(Class boClass, String selectFrom, String whereStatement, String orderStatement, int start, int pageSize) throws FindException {
		StringBuilder sb = new StringBuilder();
		if(StringUtils.isEmpty(selectFrom)) throw new IllegalArgumentException("selectFrom is empty!");
		if(selectFrom.toLowerCase().indexOf("select") < 0){
			sb.append("select {t.*} ");
			if(selectFrom.toLowerCase().indexOf("from") < 0){
				sb.append("from ").append(selectFrom).append(" t ");
			}
		} else {
			sb.append(selectFrom);
		}
		if(!StringUtils.isEmpty(whereStatement)){
			if(whereStatement.toLowerCase().indexOf("where") < 0){
				sb.append(" where ");
			}
			sb.append(whereStatement);
		}
		if(!StringUtils.isEmpty(orderStatement)){
			sb.append(orderStatement);
		}
		log.debug(sb);
		List list = null;
		try{
			EntityManager em = this.getEntityManager();
			Query query = em.createNativeQuery(sb.toString(), boClass)
							.setFirstResult(start)
							.setMaxResults(pageSize);
			list = query.getResultList();
		} catch (Exception e){
			throw new FindException(e);
		}
		return list;
	}
	
	// ------------------------------
	abstract class JpaCallBackAction implements JpaCallback {
		private int start;

		private int pageSize;

		private String queryString;

		private String[] paramNames;

		private Object[] paramValues;

		public JpaCallBackAction(String queryString, int start, int pageSize) {
			this.queryString = queryString;
			this.start = start;
			this.pageSize = pageSize;
		}

		public JpaCallBackAction(String queryString, Object[] paramValues,
				int start, int pageSize) {
			this.queryString = queryString;
			this.paramValues = paramValues;
			this.start = start;
			this.pageSize = pageSize;
		}

		public JpaCallBackAction(String queryString, String[] paramNames,
				Object[] paramValues, int start, int pageSize) {
			this.queryString = queryString;
			this.paramNames = paramNames;
			this.paramValues = paramValues;
			this.start = start;
			this.pageSize = pageSize;
		}

		public int getStart() {
			return this.start;
		}

		public int getRowsPerPage() {
			return this.pageSize;
		}

		public String getQueryString() {
			return this.queryString;
		}

		public Object[] getParamValues() {
			return this.paramValues;
		}

		public String[] getParamNames() {
			return this.paramNames;
		}

		/* (non-Javadoc)
		 * @see org.springframework.orm.jpa.JpaCallback#doInJpa(javax.persistence.EntityManager)
		 */
		abstract public Object doInJpa(EntityManager arg0) throws PersistenceException;
		
	}

	public List<T> getNamedQueryResult(String queryName, Object[] params, int start, int pageSize) throws FindException {
		// TODO Auto-generated method stub
		return null;
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#getNamedQueryResult(java.lang.String, java.lang.String[], java.lang.Object[], int, int)
	 */
	public List<T> getNamedQueryResult(String queryName, String[] paramNames, Object[] params, int start, int pageSize) throws FindException {
		// TODO Auto-generated method stub
		return null;
	}


}
